xm,xend: Add new option "change_home_server" to xm migrate
authorKeir Fraser <keir.fraser@citrix.com>
Wed, 14 Oct 2009 08:09:23 +0000 (09:09 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Wed, 14 Oct 2009 08:09:23 +0000 (09:09 +0100)
This patch adds a new option to xm migrate command.  A concept of the
option is inspired from XenServer/XenCenter.  The concept is "Change
home server (affinity)."  The option name is "change_home_server."

Currently, a config.sxp file of a managed domain is not migrated to a
destination server even if the migration of the managed domain
succeeds.  The config.sxp file is kept in a source server.

By the patch, the config.sxp file is migrated with the managed domain.
The config.sxp file is unregistered from the source server, then the
config.sxp file is registered to the destination server.

BTW, should the config.sxp file be always migrated without the option?
If the managed domain is migrated without the option, the managed
domains exist on both the source server and the destination server.
(Of course, the managed domain on the source server is "halted" state,
and the managed domain on the destination server is "running" state.)
Is it good that the managed domains with a same UUID exist on both
servers?  (In the patch, I added the option for compatible.)

Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
tools/python/xen/xend/XendConfig.py
tools/python/xen/xend/XendDomain.py
tools/python/xen/xend/XendDomainInfo.py
tools/python/xen/xm/migrate.py

index 50b7afad41d570d6d8fd3caf24ef6172824d5c16..b94175cd47d1c5da5077504efa6c0fe51e0a33bd 100644 (file)
@@ -957,6 +957,7 @@ class XendConfig(dict):
         _set_cfg_if_exists('on_xend_stop')
         _set_cfg_if_exists('on_xend_start')
         _set_cfg_if_exists('vcpu_avail')
+        _set_cfg_if_exists('change_home_server')
         
         # Parse and store runtime configuration 
         _set_cfg_if_exists('start_time')
@@ -1158,6 +1159,9 @@ class XendConfig(dict):
         if 'cpuid_check' in self:
             self.cpuid_to_sxp(sxpr, 'cpuid_check')
 
+        if self.has_key('change_home_server'):
+            sxpr.append(['change_home_server', self['change_home_server']])
+
         log.debug(sxpr)
 
         return sxpr    
index 336e5bc8b2dc5440757c3478bef6744320def20c..9c8516de270dc80cab5d04ba24e4c61037d5075e 100644 (file)
@@ -1156,7 +1156,21 @@ class XendDomain:
             self.policy_lock.acquire_reader()
 
             try:
-                return XendCheckpoint.restore(self, fd, paused=paused, relocating=relocating)
+                dominfo = XendCheckpoint.restore(self, fd, paused=paused, relocating=relocating)
+                if relocating and \
+                   dominfo.info.has_key("change_home_server"):
+                    chs = (dominfo.info["change_home_server"] == "True")
+                    dominfo.setChangeHomeServer(None)
+                    if chs:
+                        self.domains_lock.acquire()
+                        try:
+                            log.debug("Migrating new managed domain: %s: %s" %
+                                      (dominfo.getName(), dominfo.get_uuid()))
+                            self._managed_domain_register(dominfo)
+                            self.managed_config_save(dominfo)
+                        finally:
+                            self.domains_lock.release()
+                return dominfo
             except XendError, e:
                 log.exception("Restore failed")
                 raise
@@ -1298,7 +1312,8 @@ class XendDomain:
 
         return val       
 
-    def domain_migrate(self, domid, dst, live=False, port=0, node=-1, ssl=None):
+    def domain_migrate(self, domid, dst, live=False, port=0, node=-1, ssl=None,\
+                       chs=False):
         """Start domain migration.
         
         @param domid: Domain ID or Name
@@ -1313,6 +1328,8 @@ class XendDomain:
         @type node: int
         @keyword ssl: use ssl connection
         @type ssl: bool
+        @keyword chs: change home server for managed domain
+        @type chs: bool
         @rtype: None
         @raise XendError: Failed to migrate
         @raise XendInvalidDomain: Domain is not valid
@@ -1328,6 +1345,8 @@ class XendDomain:
             raise VMBadState("Domain is not running",
                              POWER_STATE_NAMES[DOM_STATE_RUNNING],
                              POWER_STATE_NAMES[dominfo._stateGet()])
+        if chs and not self.is_domain_managed(dominfo):
+            raise XendError("Domain is not a managed domain")
 
         """ The following call may raise a XendError exception """
         dominfo.testMigrateDevices(True, dst)
@@ -1339,120 +1358,131 @@ class XendDomain:
         if ssl is None:
             ssl = xoptions.get_xend_relocation_ssl()
 
-        if ssl:
-            from OpenSSL import SSL
-            from xen.web import connection
-            if port == 0:
-                port = xoptions.get_xend_relocation_ssl_port()
-            try:
-                ctx = SSL.Context(SSL.SSLv23_METHOD)
-                sock = SSL.Connection(ctx,
-                           socket.socket(socket.AF_INET, socket.SOCK_STREAM))
-                sock.set_connect_state()
-                sock.connect((dst, port))
-                sock.send("sslreceive\n")
-                sock.recv(80)
-            except SSL.Error, err:
-                raise XendError("SSL error: %s" % err)
-            except socket.error, err:
-                raise XendError("can't connect: %s" % err)
-
-            p2cread, p2cwrite = os.pipe()
-            threading.Thread(target=connection.SSLSocketServerConnection.fd2send,
-                             args=(sock, p2cread)).start()
+        try:
+            dominfo.setChangeHomeServer(chs)
+            if ssl:
+                self._domain_migrate_by_ssl(dominfo, dst, live, port, node)
+            else:
+                self._domain_migrate(dominfo, dst, live, port, node)
+        except:
+            dominfo.setChangeHomeServer(None)
+            raise
 
+    def _domain_migrate_by_ssl(self, dominfo, dst, live, port, node):
+        from OpenSSL import SSL
+        from xen.web import connection
+        if port == 0:
+            port = xoptions.get_xend_relocation_ssl_port()
+        try:
+            ctx = SSL.Context(SSL.SSLv23_METHOD)
+            sock = SSL.Connection(ctx,
+                       socket.socket(socket.AF_INET, socket.SOCK_STREAM))
+            sock.set_connect_state()
+            sock.connect((dst, port))
+            sock.send("sslreceive\n")
+            sock.recv(80)
+        except SSL.Error, err:
+            raise XendError("SSL error: %s" % err)
+        except socket.error, err:
+            raise XendError("can't connect: %s" % err)
+
+        p2cread, p2cwrite = os.pipe()
+        threading.Thread(target=connection.SSLSocketServerConnection.fd2send,
+                         args=(sock, p2cread)).start()
+
+        try:
             try:
+                XendCheckpoint.save(p2cwrite, dominfo, True, live, dst,
+                                    node=node)
+            except Exception, ex:
+                m_dsterr = None
                 try:
-                    XendCheckpoint.save(p2cwrite, dominfo, True, live, dst,
-                                        node=node)
-                except Exception, ex:
-                    m_dsterr = None
-                    try:
-                        sock.settimeout(3.0)
-                        dsterr = sock.recv(1024)
-                        sock.settimeout(None)
-                        if dsterr:
-                            # See send_error@relocate.py. If an error occurred
-                            # in a destination side, an error message with the
-                            # following form is returned from the destination
-                            # side.
-                            m_dsterr = \
-                                re.match(r"^\(err\s\(type\s(.+)\)\s\(value\s'(.+)'\)\)", dsterr)
-                    except:
-                        # Probably socket.timeout exception occurred.
-                        # Ignore the exception because it has nothing to do with
-                        # an exception of XendCheckpoint.save.
-                        pass
-
-                    if m_dsterr:
-                        raise XendError("%s (from %s)" % (m_dsterr.group(2), dst))
-                    raise
-            finally:
-                try:
-                    sock.shutdown(2)
+                    sock.settimeout(3.0)
+                    dsterr = sock.recv(1024)
+                    sock.settimeout(None)
+                    if dsterr:
+                        # See send_error@relocate.py. If an error occurred
+                        # in a destination side, an error message with the
+                        # following form is returned from the destination
+                        # side.
+                        m_dsterr = \
+                            re.match(r"^\(err\s\(type\s(.+)\)\s\(value\s'(.+)'\)\)", dsterr)
                 except:
-                    # Probably the socket is already disconnected by sock.close
-                    # in the destination side.
+                    # Probably socket.timeout exception occurred.
                     # Ignore the exception because it has nothing to do with
                     # an exception of XendCheckpoint.save.
                     pass
-                sock.close()
 
-            os.close(p2cread)
-            os.close(p2cwrite)
-        else:
-            if port == 0:
-                port = xoptions.get_xend_relocation_port()
+                if m_dsterr:
+                    raise XendError("%s (from %s)" % (m_dsterr.group(2), dst))
+                raise
+        finally:
             try:
-                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-                # When connecting to our ssl enabled relocation server using a
-                # plain socket, send will success but recv will block. Add a
-                # 30 seconds timeout to raise a socket.timeout exception to
-                # inform the client.
-                sock.settimeout(30.0)
-                sock.connect((dst, port))
-                sock.send("receive\n")
-                sock.recv(80)
-                sock.settimeout(None)
-            except socket.error, err:
-                raise XendError("can't connect: %s" % err)
+                sock.shutdown(2)
+            except:
+                # Probably the socket is already disconnected by sock.close
+                # in the destination side.
+                # Ignore the exception because it has nothing to do with
+                # an exception of XendCheckpoint.save.
+                pass
+            sock.close()
+
+        os.close(p2cread)
+        os.close(p2cwrite)
+
+    def _domain_migrate(self, dominfo, dst, live, port, node):
+        if port == 0:
+            port = xoptions.get_xend_relocation_port()
+        try:
+            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            # When connecting to our ssl enabled relocation server using a
+            # plain socket, send will success but recv will block. Add a
+            # 30 seconds timeout to raise a socket.timeout exception to
+            # inform the client.
+            sock.settimeout(30.0)
+            sock.connect((dst, port))
+            sock.send("receive\n")
+            sock.recv(80)
+            sock.settimeout(None)
+        except socket.error, err:
+            raise XendError("can't connect: %s" % err)
 
+        try:
             try:
+                XendCheckpoint.save(sock.fileno(), dominfo, True, live,
+                                    dst, node=node)
+            except Exception, ex:
+                m_dsterr = None
                 try:
-                    XendCheckpoint.save(sock.fileno(), dominfo, True, live,
-                                        dst, node=node)
-                except Exception, ex:
-                    m_dsterr = None
-                    try:
-                        sock.settimeout(3.0)
-                        dsterr = sock.recv(1024)
-                        sock.settimeout(None)
-                        if dsterr:
-                            # See send_error@relocate.py. If an error occurred
-                            # in a destination side, an error message with the
-                            # following form is returned from the destination
-                            # side.
-                            m_dsterr = \
-                                re.match(r"^\(err\s\(type\s(.+)\)\s\(value\s'(.+)'\)\)", dsterr)
-                    except:
-                        # Probably socket.timeout exception occurred.
-                        # Ignore the exception because it has nothing to do with
-                        # an exception of XendCheckpoint.save.
-                        pass
-
-                    if m_dsterr:
-                        raise XendError("%s (from %s)" % (m_dsterr.group(2), dst))
-                    raise
-            finally:
-                try:
-                    sock.shutdown(2)
+                    sock.settimeout(3.0)
+                    dsterr = sock.recv(1024)
+                    sock.settimeout(None)
+                    if dsterr:
+                        # See send_error@relocate.py. If an error occurred
+                        # in a destination side, an error message with the
+                        # following form is returned from the destination
+                        # side.
+                        m_dsterr = \
+                            re.match(r"^\(err\s\(type\s(.+)\)\s\(value\s'(.+)'\)\)", dsterr)
                 except:
-                    # Probably the socket is already disconnected by sock.close
-                    # in the destination side.
+                    # Probably socket.timeout exception occurred.
                     # Ignore the exception because it has nothing to do with
                     # an exception of XendCheckpoint.save.
                     pass
-                sock.close()
+
+                if m_dsterr:
+                    raise XendError("%s (from %s)" % (m_dsterr.group(2), dst))
+                raise
+        finally:
+            try:
+                sock.shutdown(2)
+            except:
+                # Probably the socket is already disconnected by sock.close
+                # in the destination side.
+                # Ignore the exception because it has nothing to do with
+                # an exception of XendCheckpoint.save.
+                pass
+            sock.close()
 
     def domain_save(self, domid, dst, checkpoint=False):
         """Start saving a domain to file.
index fb76b47ca658a8f5d9daf3b3a1ad830e8bcbc7e6..5e9f0fe6e12ecf0e2ebf85529bf5425e8f6e09af 100644 (file)
@@ -2364,6 +2364,13 @@ class XendDomainInfo:
         return self.getDeviceController(deviceClass).recover_migrate(
                      deviceConfig, network, dst, step, domName)
 
+    def setChangeHomeServer(self, chs):
+        if chs is not None:
+            self.info['change_home_server'] = bool(chs)
+        else:
+            if self.info.has_key('change_home_server'):
+                del self.info['change_home_server']
+
 
     ## private:
 
@@ -2870,8 +2877,10 @@ class XendDomainInfo:
         self._cleanup_phantom_devs(paths)
         self._cleanupVm()
 
-        if "transient" in self.info["other_config"] \
-           and bool(self.info["other_config"]["transient"]):
+        if ("transient" in self.info["other_config"] and \
+            bool(self.info["other_config"]["transient"])) or \
+           ("change_home_server" in self.info and \
+            bool(self.info["change_home_server"])):
             XendDomain.instance().domain_delete_by_dominfo(self)
 
 
index ee990e0b4d03aa161f246a8d520beb911595f864..220f09198a3ca5f5947bdc5cdb240f3b442dcafb 100644 (file)
@@ -51,6 +51,10 @@ gopts.opt('ssl', short='s',
           fn=set_true, default=None,
           use="Use ssl connection for migration.")
 
+gopts.opt('change_home_server', short='c',
+          fn=set_true, default=0,
+          use="Change home server for managed domains.")
+
 def help():
     return str(gopts)
     
@@ -78,4 +82,5 @@ def main(argv):
         server.xend.domain.migrate(dom, dst, opts.vals.live,
                                    opts.vals.port,
                                    opts.vals.node,
-                                   opts.vals.ssl)
+                                   opts.vals.ssl,
+                                   opts.vals.change_home_server)